let mydebug = require('../mydebug');
var events = require('events');

// const
const SYSTEM_START_DELAY_TIMEOUT = 5000;			// delay 5 seconds
const SKIMPUMP_START_DELAY_TIMEOUT = 180 * 1000;		// delay 180 seconds

const Ro_full_water_level = 250;
const Sub_full_water_level = 235;
const Ro_low_water_level = 35;

const Low_Main_Voltage = 15000;						// 15.000v is meaning lost power
const Normal_Main_Voltage = 20000;					// 20.000v is meaning power normal

const LostPower_Checktime = 10;						// 10 second(check per second, and continue 10)
const NormalPower_Checktime = 10;

let PowerState = {
	NORMAL:	0,
	LOST:	1
}
let powerState = PowerState.NORMAL;
let powerCheckCounter = 0;
let Power_CheckTimerId = null;

// timer id
let Skimpump_timeout_id = null;

// status
let PowerMode = {
	DC:		0,
	BAT:	1
};

let controllerData = {
	ro_level 		: 0,
	sub_level 		: 0,
	ro_ext_water 	: false,

	ro_emergen 	: false,
	sub_emergen	: false,

	main_pump 	: false,
	skim_pump 	: false,
	wave_pump 	: false,
	wave_bak	: false,
	sea_pump	: false,
	ro_pump		: false,
	ro_ext_pump	: false,
	aux_light	: false,

	main_voltage	: 0,
	battery_voltage : 0,
	main_current	: 0,
	main_pump_c		: 0,
	skim_pump_c		: 0,

	bat_24v			: false,
	dc_bat			: PowerMode.DC,

	temperature		: 0
}

let cmd_id = 0;

let WorkModes = {
	RUNNING:		0,		// Normal, running
	PAUSE:			1,
	SUB_REFILL:		2,
	RO_REFILL:		3
};

var ControllerEvent = new events.EventEmitter();

let workMode = WorkModes.PAUSE;

setTimeout(function() {
	setWorkmode(WorkModes.RUNNING)
}, SYSTEM_START_DELAY_TIMEOUT);

const SerialPort = require('serialport');
const Readline = require('@serialport/parser-readline');
const controller = new SerialPort('/dev/ttyS0', {baudRate: 115200});

const parser = controller.pipe(new Readline({ delimiter: '\r\n' }));
parser.on('data', receiveMsg);

ControllerEvent.on("workmode", WorkmodeChange);

ControllerEvent.on("rolevel", RoLevelChange);
ControllerEvent.on("sublevel", SubLevelChange);
ControllerEvent.on("roextwater", RoExtHaveWater);

ControllerEvent.on("mainpump", MainpumpChange);
ControllerEvent.on("skimpump", SkimpumpChange);

ControllerEvent.on("mainc", MainCurrentChange)
ControllerEvent.on("mainpumpc", MainPumpCurrentChange);
ControllerEvent.on("skimpumpc", SkimPumpCurrentChange);

ControllerEvent.on("mainvol", MainPowerChange);
ControllerEvent.on("batvol", BatteryPowerChange);


// command count
function getCmdId() {
	cmd_id++;
	if (cmd_id == 100000000) {
		cmd_id = 1;
	}
	return cmd_id;
}

// ------------- Data update -----------------
function updateData(v, name, event) {
	if ((v == null) || (v == controllerData[name])) {
		return;
	}

	controllerData[name] = v;
	ControllerEvent.emit(event, v);
}

// -----------------------------------------------
function SendResponse(msg) {
	let resp = {a: 'rep', id: msg.id, data: {}};
	let s = JSON.stringify(resp) + "\r\n";
	controller.write(s);
}

function Cmd_Report(msg) {
	let data = msg.data;

	updateData(data.ro, "ro_level", "rolevel");
	updateData(data.sub, "sub_level", "sublevel");

	updateData(data.roew, "ro_ext_water", "roextwater")
	updateData(data.roem, "ro_emergen", "roemergen");
	updateData(data.subem, "sub_emergen", "subemergen");
	
	updateData(data.temp, "temperature", "temperature");

	updateData(data.mv, "main_voltage", "mainvol");
	updateData(data.bv, "battery_voltage", "batvol");
	updateData(data.mc, "main_current", "mainc");
	updateData(data.mpc, "main_pump_c", "mainpumpc");
	updateData(data.spc, "skim_pump_c", "skimpumpc");

	updateData(data.bat24, "bat_24v", "bat24v");
	updateData(data.dcbat, "dc_bat", "dcbat");

	updateData(data.mp, "main_pump", "mainpump");
	updateData(data.sp, "skim_pump", "skimpump");
	updateData(data.wp, "wave_pump", "wavepump");
	updateData(data.wpb, "wave_bak", "wavebak");
	updateData(data.seap, "sea_pump", "seapump");
	updateData(data.rp, "ro_pump", "ropump");
	updateData(data.rep, "ro_ext_pump", "roextpump");
	updateData(data.al, "aux_light", "auxlight");

	// response
	SendResponse(msg);
}

function Cmd_Reponse(msg) {

}

function setWorkmode(mode) {
	if (mode == workMode) {
		return;
	}
	workMode = mode;
	mydebug.LogOutput("New work mode:" + mode);
	ControllerEvent.emit("workmode", mode);
}

// device control
function sendCmd(data) {
	let msg = {a: 'set', id: getCmdId(), data: data};
	let s = JSON.stringify(msg) + "\r\n";
	controller.write(s);
	mydebug.LogOutput("set cmd:" + s);
}

function Aux_light_switch(on) {
	let data = {al: on};
	sendCmd(data);
}

function Sea_pump_switch(on) {
	let data = {seap: on};
	sendCmd(data);
}

function Ro_pump_switch(on) {
	let data = {rp: on};
	sendCmd(data);
}

function Ro_ext_pump_switch(on) {
	let data = {rep: on};
	sendCmd(data);
}

function Main_pump_switch(on) {
	let data = {mp: on};
	sendCmd(data);
}

function Skim_pump_switch(on) {
	if ((on == false) && (Skimpump_timeout_id != null)) {
		clearTimeout(Skimpump_timeout_id);
		Skimpump_timeout_id = null;
	}
	let data = {sp: on};
	sendCmd(data);
}

function Wave_pump_switch(on) {
	let data = {wp: on};
	sendCmd(data);
}

// Main wave pump backup mode
function Wave_pump_bak_switch(on) {
	let data = {wpb: on};
	sendCmd(data);
}

function Bat_24v_switch(on) {
	mydebug.LogOutput("Bat-24V:" + on ? "Enable" : "Disable");
	let data = {bat24: on};
	sendCmd(data);
}

function Dc_bat_switch(mode) {
	mydebug.LogOutput("Switch power to:" + mode ? "DC" : "BAT");
	let data = {dcbat: mode};
	sendCmd(data);
}

function delayStartSkimpump() {
	if (Skimpump_timeout_id != null) {
		clearTimeout(Skimpump_timeout_id);
	}

	// re-assing delay
	Skimpump_timeout_id = setTimeout(function() {
		Skim_pump_switch(true);
	}, SKIMPUMP_START_DELAY_TIMEOUT);
}

function PauseSystem() {
	setWorkmode(WorkModes.PAUSE);
}

function StartSystem() {
	setWorkmode(WorkModes.RUNNING);
}

function setWaveNightmode(on) {
	if (on) {
		// use MP40 backup mode, turn off 32v power
		Wave_pump_bak_switch(true);
		Wave_pump_switch(false);
	} else {
		// turn on 32v power
		Wave_pump_switch(true);
		// needn't turn off backup power
	}
} 


// Event function
function RoLevelChange(level) {
	// check ro level to determine to turn off ro ext pump(too high) or ro pump(too low)
	if (workMode == WorkModes.RO_REFILL) {
		// if ro level == 0, mean ro level sensor is wrong, must stop ro ext refill
		if ((level >= Ro_full_water_level) || (level == 0)) {
			setWorkmode(WorkModes.RUNNING);
			Ro_ext_pump_switch(false);
		}
	} else if (workMode == WorkModes.SUB_REFILL) {
		if (level <= Ro_low_water_level) {
			setWorkmode(WorkModes.RUNNING);
			Ro_pump_switch(false);
		}
	}
}

function SubLevelChange(level) {
	// check sub leve to determine to turn off ro pump(Sub leve is ok) or turn on ro pump(Sub level is low)
	if (workMode == WorkModes.RUNNING) {
		if ((level <= Sub_full_water_level - 10) && (controllerData.ro_level > Ro_low_water_level)) {
			setWorkmode(WorkModes.SUB_REFILL);
			Ro_pump_switch(true);
		}
	} else if (workMode == WorkModes.SUB_REFILL) {
		// sub level == 0 , mean the sub sensor is wrong, must stop sub refill
		if ((level >= Sub_full_water_level) || (level == 0)) {
			setWorkmode(WorkModes.RUNNING);
			Ro_pump_switch(false);
		}
	}
}

function RoExtHaveWater(water) {
	mydebug.LogOutput("Ro Ext have water:", water);

	// turn on or off pump, check ro level also
	if (workMode == WorkModes.RUNNING) {
		if (water && controllerData.ro_level < Ro_full_water_level) {
			setWorkmode(WorkModes.RO_REFILL);
			Ro_ext_pump_switch(true);
		}
	} else if (workMode == WorkModes.RO_REFILL) {
		if (!water) {
			setWorkmode(WorkModes.RUNNING);
			Ro_ext_pump_switch(false);
		}
	}
}

function MainpumpChange(state) {
	// when main pump is off, need check global state
	mydebug.LogOutput("Main pump state:" + state);
}

function SkimpumpChange(state) {
	// check global state
	mydebug.LogOutput("Skim pump state:" + state);
}

function WorkmodeChange(mode) {
	// workmode change
	switch (mode) {
		case WorkModes.RUNNING:
			// START devices
			Main_pump_switch(true);
			delayStartSkimpump();
			setWaveNightmode(false);
			break;
		
		case WorkModes.PAUSE:
			// STOP some devices
			Skim_pump_switch(false);
			Main_pump_switch(false);
			setWaveNightmode(true);
			break;
	}
}

function MainPowerChange(voltage) {
	if (voltage < Low_Main_Voltage) {
		if (powerState == PowerState.LOST) {
			return;
		}
		if (Power_CheckTimerId != null) {
			clearInterval(Power_CheckTimerId);
		}
		Power_CheckTimerId = setInterval(powerLost, 1000);
		powerCheckCounter = 0;
		powerState = PowerState.LOST;
		mydebug.LogOutput("Detect main power lost, checking...");
	}
	else if (voltage > Normal_Main_Voltage) {
		if (powerState == PowerState.NORMAL) {
			return;
		}
		if (Power_CheckTimerId != null) {
			clearInterval(Power_CheckTimerId);
		}
		Power_CheckTimerId = setInterval(powerNormal, 1000);
		powerCheckCounter = 0;
		powerState = PowerState.NORMAL;
		mydebug.LogOutput("Detect main power normal, checking...");
	} else {
		if (Power_CheckTimerId != null) {
			clearInterval(Power_CheckTimerId);
			Power_CheckTimerId = null;
			powerCheckCounter = 0;
			powerState = PowerState.NORMAL;
		}
	}
}

function powerLost() {
	powerCheckCounter++;
	if (powerCheckCounter < LostPower_Checktime) {
		return;
	}

	mydebug.LogOutput("Confirm main power lost, pause and switch.");
	// Power lost confirm, power failover
	PauseSystem();
	Bat_24v_switch(true);
	Dc_bat_switch(true);
	StartSystem();

	clearInterval(Power_CheckTimerId);
	Power_CheckTimerId = 0;
}

function powerNormal() {
	powerCheckCounter++;
	if (powerCheckCounter < NormalPower_Checktime) {
		return;
	}

	mydebug.LogOutput("Confirm main power normal, pause and switch.");
	// Power normal confirm, power normal
	PauseSystem();
	Dc_bat_switch(false);
	Bat_24v_switch(false);
	StartSystem();

	clearInterval(Power_CheckTimerId);
	Power_CheckTimerId = 0;
}

function BatteryPowerChange(voltage) {

}

function MainCurrentChange(voltage) {

}

function MainPumpCurrentChange(voltage) {

}

function SkimPumpCurrentChange(voltage) {

}

// Message process
function receiveMsg(msg) {
	// check first characters for log
	if (msg.length < 3) {
		mydebug.LogOutput("Error:", msg);
		return;
	}

	if (msg.substr(0, 3) == "###") {
		// TODO: redirect to display station. Now, just print on console.
		mydebug.LogOutput("Log:", msg.substr(3));
		return;
	}

	try {
		let data = JSON.parse(msg);
		// mydebug.LogOutput("cmd:", data);

		let action = data.a;

		switch (action) {
			case "rep":
				Cmd_Report(data);
				break;

			case "resp":
				Cmd_Reponse(data);
				break;
		}
	} catch(err) {
		mydebug.LogOutput(err.name, err.message, "{", msg, "}");
	}
}

// exports
exports.ControllerEvent = ControllerEvent;
exports.getStatus = function() {
	return controllerData;
}

exports.pause = PauseSystem;

exports.start = StartSystem;

exports.sendCmd = sendCmd;

exports.setWaveNightmode = setWaveNightmode;

exports.Aux_light_switch = Aux_light_switch;
